#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
#include"head.h"

using namespace std;
uint32_t reversebit(uint32_t num)
{
    return (num & 0x000000FFU) << 24 | (num & 0x0000FF00U) << 8 | (num & 0x00FF0000U) >> 8 | (num & 0xFF000000U) >> 24;
}
uint32_t reverseint(unsigned char len)
{
    uint32_t mask=0;
    for(int i=0;i<32;i++)
    {
        mask=(mask<<1)+(i<len);
    }
    return reversebit(mask);
}

int rtnl_receive(int fd, struct msghdr *msg, int flags)
{
    int len;

    do { 
        len = recvmsg(fd, msg, flags);
    } while (len < 0 && (errno == EINTR || errno == EAGAIN));

    if (len < 0) {
        perror("Netlink receive failed");
        return -errno;
    }

    if (len == 0) { 
        perror("EOF on netlink");
        return -ENODATA;
    }

    return len;
}

static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
{
    struct iovec *iov = msg->msg_iov;
    char *buf;
    int len;

    iov->iov_base = NULL;
    iov->iov_len = 0;

    len = rtnl_receive(fd, msg, MSG_PEEK | MSG_TRUNC);

    if (len < 0) {
        return len;
    }

    buf =(char *) malloc(len);

    if (!buf) {
        perror("malloc failed");
        return -ENOMEM;
    }

    iov->iov_base = buf;
    iov->iov_len = len;

    len = rtnl_receive(fd, msg, 0);

    if (len < 0) {
        free(buf);
        return len;
    }

    *answer = buf;

    return len;
}

void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
    memset(tb, 0, sizeof(struct rtattr *) * (max + 1));

    while (RTA_OK(rta, len)) {
        if (rta->rta_type <= max) {
            tb[rta->rta_type] = rta;
        }

        rta = RTA_NEXT(rta,len);
    }
}

static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
{
    __u32 table = r->rtm_table;

    if (tb[RTA_TABLE]) {
        table = *(__u32 *)RTA_DATA(tb[RTA_TABLE]);
    }

    return table;
}

void print_route(nlmsghdr *nl_header_answer)
{
    rtmsg* r =(rtmsg *)NLMSG_DATA(nl_header_answer);
    int len = nl_header_answer->nlmsg_len;
    rtattr* tb[RTA_MAX+1];
    int table;
    char buf[256];
    len -= NLMSG_LENGTH(sizeof(*r));
    if(len<0)
    {
        perror("错误的长度");
        return ;
    }
    parse_rtattr(tb,RTA_MAX,RTM_RTA(r),len);
    table = rtm_get_table(r,tb);
    if(r->rtm_family!=AF_INET&&table!=RT_TABLE_MAIN)
    {
        return ;
    }
    in_addr ip4;
    if(tb[RTA_DST])
    {
        if (r->rtm_dst_len <16)
        {
            return;
        }
        ip4.s_addr=reverseint(r->rtm_dst_len);
        fprintf(stdout,"\"%s\",", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_DST]), buf, sizeof(buf)));
        fprintf(stdout,"\"%s\", ",inet_ntop(r->rtm_family,(void *)&ip4,buf,sizeof(buf)));
    }
    else if(r->rtm_dst_len)
    {
        ip4.s_addr=reverseint(r->rtm_dst_len);
        fprintf(stdout,"\"0.0.0.0\",\"%s\",",inet_ntop(r->rtm_family,(void *)&ip4,buf,sizeof(buf)));
    }
    else
    {
        fprintf(stdout,"\"default\",\"0.0.0.0\",");
    }
    if(tb[RTA_GATEWAY])
    {
        fprintf(stdout,"\"%s\",",inet_ntop(r->rtm_family,RTA_DATA(tb[RTA_GATEWAY]),buf,sizeof(buf)));
    }
    else
    {
        fprintf(stdout,"\"0.0.0.0\",");
    }
    if(tb[RTA_OIF])
    {
        char if_nam_buf[IF_NAMESIZE];
        int ifidx = *(uint32_t *)RTA_DATA(tb[RTA_OIF]);
        fprintf(stdout,"\"%s\",", if_indextoname(ifidx, if_nam_buf));
    }
    if (tb[RTA_MULTIPATH])
        fprintf(stdout,"\"%d\"",*(int *)RTA_DATA(tb[RTA_MULTIPATH]));
    if(tb[RTA_METRICS])
        fprintf(stdout,"\"%d\"",*(int *)RTA_DATA(tb[RTA_METRICS]));
    else
        fprintf(stdout,"\"0\"");
}

int open_netlink()
{
    struct sockaddr_nl saddr;

    int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

    if (sock < 0) {
        perror("Failed to open netlink socket");
        return -1;
    }

    memset(&saddr, 0, sizeof(saddr));

    saddr.nl_family = AF_NETLINK;
    saddr.nl_pid = getpid();

    if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
        perror("Failed to bind to netlink socket");
        close(sock);
        return -1;
    }

    return sock;
}

int do_route_dump_requst(int sock)
{
    struct {
        struct nlmsghdr nlh;
        struct rtmsg rtm;
    } nl_request;

    nl_request.nlh.nlmsg_type = RTM_GETROUTE;
    nl_request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    nl_request.nlh.nlmsg_len = sizeof(nl_request);
    nl_request.nlh.nlmsg_seq = time(NULL);
    nl_request.rtm.rtm_family = AF_INET;

    return send(sock, &nl_request, sizeof(nl_request), 0);
}

int get_route_dump_response(int sock)
{
    struct sockaddr_nl nladdr;
    struct iovec iov;
    struct msghdr msg = {
        .msg_name = &nladdr,
        .msg_namelen = sizeof(nladdr),
        .msg_iov = &iov,
        .msg_iovlen = 1,
    };

    char *buf;
    int dump_intr = 0;

    int status = rtnl_recvmsg(sock, &msg, &buf);

    struct nlmsghdr *h = (struct nlmsghdr *)buf;
    int msglen = status;

    //printf("Main routing table IPv4\n");
    fprintf(stdout,"{\"res\":[");
    while (NLMSG_OK(h, msglen)) {
        if (h->nlmsg_flags & NLM_F_DUMP_INTR) {
            fprintf(stderr, "Dump was interrupted\n");
            free(buf);
            return -1;
        }

        if (nladdr.nl_pid != 0) {
            continue;
        }

        if (h->nlmsg_type == NLMSG_ERROR) {
            perror("netlink reported error");
            free(buf);
        }
        fprintf(stdout,"[");
        print_route(h);

        h = NLMSG_NEXT(h, msglen);
        if(NLMSG_OK(h, msglen))
            fprintf(stdout,"],");
        else
            fprintf(stdout,"]");
    }
    fprintf(stdout,"]}");
    free(buf);

    return status;
}

int main()
{
    int nl_sock = open_netlink();

    if (do_route_dump_requst(nl_sock) < 0) {
        perror("Failed to perfom request");
        close(nl_sock);
        return -1;
    }

    get_route_dump_response(nl_sock);

    close (nl_sock);

    return 0;
}